package org.ysling.litemall.core.redis.config;
// Copyright (c) [ysling] [927069313@qq.com]
// [litemall-plus] is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
//             http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.CacheErrorHandler;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisOperations;

import javax.annotation.Nonnull;
import java.time.Duration;

/**
 * 自定义redis配置
 *
 * @author Ysling
 */

@Slf4j
@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
public class RedisConfigurationSupport extends CachingConfigurerSupport {

    /**
     * 当有多个管理器的时候，必须使用该注解在一个管理器上注释：表示该管理器为默认的管理器
     * @return 缓存
     */
    @Bean
    @Primary
    public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofDays(-1))
                .computePrefixWith(name -> name + ":");
        // 注入cacheManager
        return RedisCacheManager.RedisCacheManagerBuilder
                .fromCacheWriter(redisCacheWriter(connectionFactory))
                .cacheDefaults(config)
                .build();
    }


    @Bean
    public RedisCacheWriter redisCacheWriter(RedisConnectionFactory redisConnectionFactory) {
        return new CustomRedisCacheWriter(redisConnectionFactory);
    }

    /**
     * 自定义缓存key生成策略，默认key生成策略
     * target: 类
     * method: 方法
     * params: 参数
     * @return KeyGenerator
     * 注意: 如果是自定义方法名不是  keyGenerator 则该方法只是声明了key的生成策略,
     * 还未被使用,需在@Cacheable注解中指定keyGenerator
     * 如: @Cacheable(value = "key", keyGenerator = "cacheKeyGenerator")
     */
    @Bean
    @Override
    public KeyGenerator keyGenerator() {
        return (target, method, params) -> {
            StringBuilder sb = new StringBuilder();
            sb.append(method.getName()).append(":");
            for (Object obj : params) {
                // 由于参数可能不同, hashCode肯定不一样, 缓存的key也需要不一样
                sb.append(JSON.toJSONString(obj).hashCode());
            }
            return sb.toString();
        };
    }


    @Bean
    @Primary
    @Override
    public CacheErrorHandler errorHandler() {
        //异常处理，当Redis发生异常时，打印日志，但是程序正常走
        log.info("初始化 -> [Redis CacheErrorHandler]");
        return new CacheErrorHandler() {
            @Override
            public void handleCacheGetError(@Nonnull RuntimeException e, @Nonnull Cache cache, @Nonnull Object key) {
                log.error("Redis occur handleCacheGetError：key -> "+key, e);
            }

            @Override
            public void handleCachePutError(@Nonnull RuntimeException e,@Nonnull Cache cache,@Nonnull Object key, Object value) {
                log.error("Redis occur handleCachePutError：key -> "+key+"；value -> " +value, e);
            }

            @Override
            public void handleCacheEvictError(@Nonnull RuntimeException e,@Nonnull Cache cache,@Nonnull Object key) {
                log.error("Redis occur handleCacheEvictError：key -> "+key, e);
            }

            @Override
            public void handleCacheClearError(@Nonnull RuntimeException e,@Nonnull Cache cache) {
                log.error("Redis occur handleCacheClearError：", e);
            }
        };
    }

}
